package model

import (
	"encoding/json"
	"fmt"
	"github.com/google/go-cmp/cmp"
	"testing"

	"go.mongodb.org/mongo-driver/bson"
)

type (
	port_configuration_suit struct {
		value    string
		expected PortConfiguration
	}
)

var (
	int32Value1        int32 = 1
	int32Value65532    int32 = 65532
	strValueDoublePort       = "80:80"
	strValueSinglePort       = "80"
	structValueGeneric       = Port{
		Mode:      "mode",
		Target:    1,
		Published: 1,
		Protocol:  "http",
	}
)

func TestPortWrapper_JSON(t *testing.T) {
	TestJsonSuits(
		t,
		[]Suit{
			{&PortWrapper{}, "null"},
			{&PortWrapper{IntValue: &int32Value1}, "1"},
			{&PortWrapper{IntValue: &int32Value65532}, "65532"},
			{&PortWrapper{StringValue: &strValueDoublePort}, "\"80:80\""},
			{&PortWrapper{StringValue: &strValueSinglePort}, "\"80\""},
			{&PortWrapper{StructValue: &structValueGeneric}, "{\"mode\":\"mode\",\"target\":1,\"published\":1,\"protocol\":\"http\"}"},
		}, func(bytes []byte) (interface{}, error) {
			var reverse PortWrapper
			err := json.Unmarshal(bytes, &reverse)

			return &reverse, err
		},
	)
}

func TestPortWrapper_BSON(t *testing.T) {
	TestBsonSuits(
		t,
		[]Suit{
			{&PortWrapper{}, "null"},
			{&PortWrapper{IntValue: &int32Value1}, "{\"$numberInt\":\"1\"}"},
			{&PortWrapper{IntValue: &int32Value65532}, "{\"$numberInt\":\"65532\"}"},
			{&PortWrapper{StringValue: &strValueDoublePort}, "\"80:80\""},
			{&PortWrapper{StringValue: &strValueSinglePort}, "\"80\""},
			{&PortWrapper{StructValue: &structValueGeneric}, "{\"mode\": \"mode\",\"target\": {\"$numberInt\":\"1\"},\"published\": {\"$numberInt\":\"1\"},\"protocol\": \"http\"}"},
		},
		func(value bson.RawValue) (interface{}, error) {
			var reverse PortWrapper
			err := value.Unmarshal(&reverse)

			return &reverse, err
		},
	)
}

func TestPortConfiguration(t *testing.T) {
	suits := []port_configuration_suit{
		{"3000", PortConfiguration{ContainerPort: 3000, HostPort: 3000, Protocol: ProtocolTcp}},
		{"8000:8000", PortConfiguration{ContainerPort: 8000, HostPort: 8000, Protocol: ProtocolTcp}},
		{"49100:22", PortConfiguration{ContainerPort: 22, HostPort: 49100, Protocol: ProtocolTcp}},
		{"127.0.0.1:49100:22", PortConfiguration{ContainerPort: 22, HostPort: 49100, Protocol: ProtocolTcp}},
		{"3000/tcp", PortConfiguration{ContainerPort: 3000, HostPort: 3000, Protocol: ProtocolTcp}},
		{"8000:8000/tcp", PortConfiguration{ContainerPort: 8000, HostPort: 8000, Protocol: ProtocolTcp}},
		{"49100:22/tcp", PortConfiguration{ContainerPort: 22, HostPort: 49100, Protocol: ProtocolTcp}},
		{"127.0.0.1:49100:22/tcp", PortConfiguration{ContainerPort: 22, HostPort: 49100, Protocol: ProtocolTcp}},
	}

	for i, suit := range suits {
		t.Run(fmt.Sprintf("scenario-%d", i), func(t *testing.T) {
			wrapper := PortWrapper{StringValue: &suit.value}
			actual, err := wrapper.ToPortConfiguration()
			if err != nil {
				t.Fatalf("error: %s", err)
			}

			equal := cmp.Equal(&suit.expected, actual)
			if !equal {
				t.Fatalf("not equal:\n%s", cmp.Diff(&suit.expected, actual))
			}
		})

	}
}
